#include <algorithm>
#include <iostream>
#include <string>
#include "plc2llvm/Visitor/strategy/var_decls/var_decl.h"
#include "plc2llvm/Visitor/strategy/expression/expression.h"
#include "plc2llvm/utils/Log.h"
#include "plc2llvm/utils/Utils.h"
#include "plc2llvm/Semantic/SemanticError.h"
#include "plc2llvm/Semantic/SemanticWarning.h"
#include "plc2llvm/ScopeSystem/ScopeManager.h"

#define ANY_CAST_TO_OBJ(x) std::any_cast<std::shared_ptr<plcst::Object>>(x)
#define ANY_CAST_TO_TYPE(x) std::any_cast<std::shared_ptr<plcst::Type>>(x)

namespace var_decl
{
    std::any visitFunc_Var_Decls(plcst::PLCSTParser::Func_Var_DeclsContext *ctx, Visitor *visitor)
    {
        auto child = visitor->visitChildren(ctx);
        return child;
    }
    std::any visitVar_Decls(plcst::PLCSTParser::Var_DeclsContext *ctx, Visitor *visitor)
    {
        return visitor->visitChildren(ctx);
    }

    std::any visitVar_Decl_Init(plcst::PLCSTParser::Var_Decl_InitContext *ctx, Visitor *visitor)
    {
        return visitor->visitChildren(ctx);
    }

    std::any visitCommon_Var_Decl_Init(plcst::PLCSTParser::Common_Var_Decl_InitContext *ctx, Visitor *visitor)
    {
        // 获得对象名列表
        auto rawVariableNameList = visitor->visit(ctx->variable_List());
        auto variableNameList = std::any_cast<std::vector<std::string>>(rawVariableNameList);

        // 访问simple_Spec_Init|ref_Spec_Init|array_Spec_Init struct_Spec_Init|fB_Spec_Init获得对象的类型
        auto rawSymbType = visitor->visit(ctx->children.at(2));
        auto symbType = ANY_CAST_TO_TYPE(rawSymbType);

        // 填充名称、typeid DeclSymbol SymbolId
        std::vector<std::shared_ptr<plcst::Object>> variableList;
        auto currentScope = plcst::ScopeManager::getScopeManager().getCurrentScope();
        for (auto name : variableNameList)
        {
            // 填充从子树获得的信息
            std::shared_ptr<plcst::Object> newobjsymb = std::make_shared<plcst::Object>(symbType, std::string(name));
            // 加入到当前作用域、GlobalTable中
            variableList.push_back(newobjsymb);
            currentScope->insert<plcst::Object>(newobjsymb);

            // codegen
            auto var_ptr = visitor->builder->CreateAlloca(visitor->builder->getInt32Ty(), nullptr, name);
            visitor->builder->CreateStore(symbType->initllvmValue, var_ptr);
            newobjsymb->llvmval = var_ptr;
            //--------------------------------------------------------------------------------------
        }

        return variableList;
    }

    std::any visitSimple_Spec_Init(plcst::PLCSTParser::Simple_Spec_InitContext *ctx, Visitor *visitor)
    {
        auto raw_symb = visitor->visit(ctx->simple_Spec());
        auto symb = ANY_CAST_TO_TYPE(raw_symb);
        if (ctx->constant_Expr() != nullptr)
        {
            auto raw_constantExpr = visitor->visit(ctx->constant_Expr());
            auto constantExpr = ANY_CAST_TO_OBJ(raw_constantExpr);
            auto constantExprType = constantExpr->getType();
            // 类型检查
            auto msg = constantExprType->canConvertTo(symb);
            switch (msg)
            {
            case plcst::ConvertionSignal::IMPLICIT_CONVERSION:
                break;
            case plcst::ConvertionSignal::EXPLICIT_CONVERSION:
                // 给出一个需要显式转换的错误
                throw SemanticError(visitor->getTokenStream(), ctx, "Explicit type conversion is required");
                break;
            case plcst::ConvertionSignal::ERROR_CONVERSION:
                // 报一个类型不合理，无法赋值的错误
                throw SemanticError(visitor->getTokenStream(), ctx, "Illegal type, cannot be assigned");
                break;
            default:
                throw SemanticError(visitor->getTokenStream(), ctx, "unknow error");
                break;
            }
            // codegen
            auto castedValue = constantExprType->castTo(symb, constantExpr->llvmval, visitor->builder);
            symb = symb->clone();
            symb->initllvmValue = castedValue;
        }
        return symb;
    }

    std::any visitSimple_Spec(plcst::PLCSTParser::Simple_SpecContext *ctx, Visitor *visitor)
    {
        return visitor->visitChildren(ctx);
    }

    std::any visitElem_Type_Name(plcst::PLCSTParser::Elem_Type_NameContext *ctx, Visitor *visitor)
    {
        auto typeName = ctx->getText();
        auto type = plcst::ScopeManager::getScopeManager().getGlobalScope()->find<plcst::Type>(typeName);
        return type;
    }

    std::any visitVariable_List(plcst::PLCSTParser::Variable_ListContext *ctx, Visitor *visitor)
    {
        std::vector<std::string> variable_list;
        for (auto child : ctx->variable_Name())
        {
            variable_list.push_back(child->getText());
        }
        return variable_list;
    }

    std::any visitVariable_Name(plcst::PLCSTParser::Variable_NameContext *ctx, Visitor *visitor)
    {
        return visitor->visit(ctx->identifier());
    }

    std::any visitNumeric_Type_Name(plcst::PLCSTParser::Numeric_Type_NameContext *ctx, Visitor *visitor)
    {
        auto typeName = ctx->getText();
        auto raw_typeSymbol = plcst::ScopeManager::getScopeManager().find<plcst::Type>(typeName);
        // catch undefined error
        if (raw_typeSymbol == nullptr)
            throw SemanticError(visitor->getTokenStream(), ctx, "Type undefined");
        else
            return raw_typeSymbol;
    }
}